﻿using System;
using System.Collections.Generic;

using System.Text;
using System.IO;
using System.Web;
using System.Net;
using System.Threading;

namespace MSL.Utility.Net
{
    /// <summary>
    /// 下载文件类
    /// </summary>
    public class DownFile
    {
        #region 文件下载
        /// <summary>
        /// 普通下载 
        /// </summary>
        /// <param name="path">文件路径</param>
        public static void DownloadFile(string path)
        {
            //string path = HttpContext.Current.Server.MapPath(fileName);
            if (File.Exists(path))
            {
                FileInfo fi = new FileInfo(path);
                HttpContext.Current.Response.Clear();
                HttpContext.Current.Response.ClearHeaders();
                HttpContext.Current.Response.Buffer = false;
                HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(Path.GetFileName(path), System.Text.Encoding.UTF8));
                HttpContext.Current.Response.AppendHeader("Content-Length", fi.Length.ToString());
                HttpContext.Current.Response.ContentType = "application/octet-stream";
                HttpContext.Current.Response.WriteFile(path);
                HttpContext.Current.Response.Flush();
                HttpContext.Current.Response.End();
            }
        }
        /// <summary>
        /// 分块下载(重载1)
        /// </summary>
        /// <param name="fileName">文件虚拟路径</param>
        /// <param name="size">块大小（单位为：b字节）</param>
        public static void DownloadFile(string fileName, long size)
        {
            string path = HttpContext.Current.Server.MapPath(fileName);
            byte[] buffer = new byte[size]; //建立一个200K的缓冲区 
            long readData = 0;                 //已读的字节数   
            FileStream stream = null;
            try
            {
                //打开文件   
                stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                readData = stream.Length;

                //添加Http头   
                HttpContext.Current.Response.ContentType = "application/octet-stream";
                HttpContext.Current.Response.AddHeader("Content-Disposition", "attachement;filename=" + HttpUtility.UrlEncode(Path.GetFileName(path)));
                HttpContext.Current.Response.AddHeader("Content-Length", readData.ToString());

                while (readData > 0)
                {
                    if (HttpContext.Current.Response.IsClientConnected)
                    {
                        int length = stream.Read(buffer, 0, System.Convert.ToInt32(size));
                        HttpContext.Current.Response.OutputStream.Write(buffer, 0, length);
                        HttpContext.Current.Response.Flush();
                        HttpContext.Current.Response.Clear();
                        readData -= length;
                    }
                    else
                    {
                        readData = -1; //防止client失去连接 
                    }
                }
            }
            catch (Exception ex)
            {
                HttpContext.Current.Response.Write("Error:" + ex.Message);
            }
            finally
            {
                if (stream != null) stream.Close();
                HttpContext.Current.Response.Close();
            }
        }
        /// <summary>
        ///  输出硬盘文件，提供下载 支持大文件、续传、速度限制、资源占用小(重载2)
        /// </summary>
        /// <param name="path">带文件名下载路径 如：http://www.baidu.com/1.rar </param>
        /// <param name="fileName">下载文件名</param>
        /// <param name="speed">每秒允许下载的字节数</param>
        /// <returns>返回是否成功</returns>
        //---------------------------------------------------------------------
        //调用：
        // string FullPath=Server.MapPath("count.txt");
        // DownloadFile("count.txt",FullPath,100);
        //---------------------------------------------------------------------
        public static bool DownloadFile(string path, string fileName, long speed)
        {
            HttpRequest request = System.Web.HttpContext.Current.Request;
            HttpResponse response = System.Web.HttpContext.Current.Response;

            try
            {
                FileStream myFile = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                BinaryReader br = new BinaryReader(myFile);
                try
                {
                    response.AddHeader("Accept-Ranges", "bytes");
                    response.Buffer = false;
                    long fileLength = myFile.Length;
                    long startBytes = 0;

                    int pack = 10240; //10K bytes
                    //int sleep = 200;   //每秒5次   即5*10K bytes每秒
                    int sleep = (int)Math.Floor((double)(1000 * pack) / (double)speed) + 1;
                    if (request.Headers["Range"] != null)
                    {
                        response.StatusCode = 206;
                        string[] range = request.Headers["Range"].Split(new char[] { '=', '-' });
                        startBytes = System.Convert.ToInt64(range[1]);
                    }
                    response.AddHeader("Content-Length", (fileLength - startBytes).ToString());
                    if (startBytes != 0)
                    {
                        response.AddHeader("Content-Range", String.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
                    }
                    response.AddHeader("Connection", "Keep-Alive");
                    response.ContentType = "application/octet-stream";
                    response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));

                    br.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                    int maxCount = (int)Math.Floor((double)(fileLength - startBytes) / pack) + 1;

                    for (int i = 0; i < maxCount; i++)
                    {
                        if (response.IsClientConnected)
                        {
                            response.BinaryWrite(br.ReadBytes(pack));
                            Thread.Sleep(sleep);
                        }
                        else
                        {
                            i = maxCount;
                        }
                    }
                }
                catch
                {
                    return false;
                }
                finally
                {
                    br.Close();
                    myFile.Close();
                }
            }
            catch
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// 下载服务器上的文件（适用于非WEB路径下，且是大文件，该方法在IE中会显示下载进度）(重载3)
        /// </summary>
        /// <param name="path">下载文件的绝对路径</param>
        /// <param name="saveName">保存的文件名，包括后缀符 如：temp.rar</param>
        public static void DownloadFile(string path, string saveName)
        {
            Stream iStream = null;

            // Buffer to read 10K bytes in chunk:
            byte[] buffer = new Byte[10240];
            int length;
            long readDate;//已读取字节
            // Identify the file name.
            string fileName = Path.GetFileName(path);

            try
            {
                iStream = new System.IO.FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                System.Web.HttpContext.Current.Response.Clear();
                readDate = iStream.Length;

                long p = 0;
                if (System.Web.HttpContext.Current.Request.Headers["Range"] != null)
                {
                    System.Web.HttpContext.Current.Response.StatusCode = 206;
                    p = long.Parse(System.Web.HttpContext.Current.Request.Headers["Range"].Replace("bytes=", "").Replace("-", ""));
                }
                if (p != 0)
                {
                    System.Web.HttpContext.Current.Response.AddHeader("Content-Range", "bytes " + p.ToString() + "-" + ((long)(readDate - 1)).ToString() + "/" + readDate.ToString());
                }
                System.Web.HttpContext.Current.Response.AddHeader("Content-Length", ((long)(readDate - p)).ToString());
                System.Web.HttpContext.Current.Response.ContentType = "application/octet-stream";
                System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(saveName, System.Text.Encoding.UTF8));

                iStream.Position = p;
                readDate = readDate - p;
                while (readDate > 0)
                {
                    if (System.Web.HttpContext.Current.Response.IsClientConnected)
                    {
                        length = iStream.Read(buffer, 0, 10240);
                        System.Web.HttpContext.Current.Response.OutputStream.Write(buffer, 0, length);
                        System.Web.HttpContext.Current.Response.Flush();

                        buffer = new Byte[10240];
                        readDate = readDate - length;
                    }
                    else
                    {
                        readDate = -1;
                    }
                }
            }
            catch (Exception ex)
            {
                System.Web.HttpContext.Current.Response.Write("Error : " + ex.Message);
            }
            finally
            {
                if (iStream != null)
                {
                    iStream.Close();
                }

                System.Web.HttpContext.Current.Response.End();
            }
        }
        #endregion

        #region 远程服务器下载文件
        /// <summary>
        /// 远程提取文件保存至服务器上(使用WebRequest)
        /// </summary>
        /// <param name="url">一个URI上的文件</param>
        /// <param name="savePath">文件保存在服务器上的全路径</param>
        public static void DownloadFileByWebRequest(string url, string savePath)
        {
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
            HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
            Stream stream = webResponse.GetResponseStream();

            int fileSize = (int)webResponse.ContentLength;
            int bufferSize = 102400;
            byte[] buffer = new byte[bufferSize];
            string dirPath = savePath.Substring(0, savePath.LastIndexOf(@"\") + 1);
            IO.DirFile.Instance.CreateFolder(dirPath);

            FileStream fileStream = File.Create(savePath, bufferSize);
            int bytesRead;

            do
            {
                bytesRead = stream.Read(buffer, 0, buffer.Length);
                fileStream.Write(buffer, 0, bytesRead);
            } while (bytesRead > 0);

            fileStream.Flush();
            fileStream.Close();
            stream.Flush();
            stream.Close();
        }

        /// <summary>
        /// 远程提取一个文件保存至服务器上(使用WebClient)
        /// </summary>
        /// <param name="url">一个URI上的文件</param>
        /// <param name="savePath">保存文件</param>
        public static void DownloadFileByWebClient(string url, string savePath)
        {
            if (String.IsNullOrEmpty(url) || String.IsNullOrEmpty(savePath))
            {
                return;
            }
            System.Net.WebClient wc = new System.Net.WebClient();
            try
            {
                string dirPath = savePath.Substring(0, savePath.LastIndexOf(@"\") + 1);
                IO.DirFile.Instance.CreateFolder(dirPath);

                //避免出现 远程服务器返回错误:(403)已禁止 错误
                wc.Credentials = CredentialCache.DefaultCredentials;//获取或这设置发送到主机并用于请求进行身份验证的网络凭据
                wc.Headers.Set("User-Agent", "Microsoft Internet Explorer");//设置标头
                wc.DownloadFile(url, savePath);
            }
            catch
            { }

            wc.Dispose();
        }
        #endregion
    }
}
